<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="HIa4M" id="HIa4M"><span data-lake-id="u35565b2f" id="u35565b2f" style="color: rgb(51, 51, 51)">典型回答</span></h1>
  <p data-lake-id="u5c1c660d" id="u5c1c660d"><span data-lake-id="uc33ba538" id="uc33ba538" style="color: rgb(51, 51, 51)">Mybatis插件的运行原理主要涉及3个关键接口：Interceptor、Invocation和Plugin。</span></p>
  <ol list="u9d3962dc">
   <li fid="u98108d56" data-lake-id="ud06ead88" id="ud06ead88"><span data-lake-id="u8f5b14bd" id="u8f5b14bd" style="color: inherit">Interceptor：拦截器接口，定义了Mybatis插件的基本功能，包括插件的初始化、插件的拦截方法以及插件的销毁方法。</span></li>
   <li fid="u98108d56" data-lake-id="u3d33edb6" id="u3d33edb6"><span data-lake-id="u4fc05bd2" id="u4fc05bd2" style="color: inherit">Invocation：调用接口，表示Mybatis在执行SQL语句时的状态，包括SQL语句、参数、返回值等信息。</span></li>
   <li fid="u98108d56" data-lake-id="ueb4294b5" id="ueb4294b5"><span data-lake-id="uc00a5c0d" id="uc00a5c0d" style="color: inherit">Plugin：插件接口，Mybatis框架在执行SQL语句时，会将所有注册的插件封装成Plugin对象，通过Plugin对象实现对SQL语句的拦截和修改。</span></li>
  </ol>
  <p data-lake-id="uc1a3668e" id="uc1a3668e"><span data-lake-id="u33e20768" id="u33e20768" style="color: rgb(51, 51, 51)">插件的运行流程如下：</span></p>
  <ol list="u69a3cf55">
   <li fid="ua192ff1d" data-lake-id="u68efcfb8" id="u68efcfb8"><span data-lake-id="u913908d9" id="u913908d9" style="color: inherit">首先，当Mybatis框架运行时，会将所有实现了Interceptor接口的插件进行初始化。</span></li>
   <li fid="ua192ff1d" data-lake-id="uf092b041" id="uf092b041"><span data-lake-id="u9f89e261" id="u9f89e261" style="color: inherit">初始化后，Mybatis框架会将所有插件和原始的Executor对象封装成一个InvocationChain对象。</span><strong><span data-lake-id="ua7ada03b" id="ua7ada03b" style="color: inherit">（这里使用的是责任链模式）</span></strong></li>
   <li fid="ua192ff1d" data-lake-id="u8addaad8" id="u8addaad8"><span data-lake-id="uda9e9409" id="uda9e9409" style="color: inherit">每次执行SQL语句时，Mybatis框架都会通过InvocationChain对象依次调用所有插件的intercept方法，实现对SQL语句的拦截和修改。</span></li>
   <li fid="ua192ff1d" data-lake-id="u4a296275" id="u4a296275"><span data-lake-id="u999df739" id="u999df739" style="color: inherit">最后，Mybatis框架会将修改后的SQL语句交给原始的Executor对象执行，并将执行结果返回给调用方。</span></li>
  </ol>
  <p data-lake-id="u16ee4cdd" id="u16ee4cdd"><span data-lake-id="u857b1b09" id="u857b1b09" style="color: rgb(51, 51, 51)">通过这种方式，Mybatis插件可以对SQL语句进行拦截和修改，实现各种功能，例如查询缓存、分页、分库分表等。</span></p>
  <h1 data-lake-id="DSba2" id="DSba2"><span data-lake-id="ua995722f" id="ua995722f" style="color: rgb(51, 51, 51)">知识扩展</span></h1>
  <h2 data-lake-id="SOdzw" id="SOdzw"><span data-lake-id="u9b2b26c7" id="u9b2b26c7">常见的Mybatis插件有哪些？</span></h2>
  <p data-lake-id="u72055d05" id="u72055d05"><span data-lake-id="u1e415f75" id="u1e415f75">常见的插件如PageHelper，用于分页使用</span></p>
  <h2 data-lake-id="EFIlO" id="EFIlO"><span data-lake-id="u9292ef1c" id="u9292ef1c">自己有写过Mybatis插件吗？</span></h2>
  <p data-lake-id="u7fd8993b" id="u7fd8993b"><span data-lake-id="uef2abf3e" id="uef2abf3e">笔者之前在做应用迁移的时候，通过Mybatis插件机制完成过一个功能，可以通过注册中心一键切换应用的数据源，伪代码如下所示：</span></p>
  <pre lang="java"><code>
@Intercepts({@Signature(method = "update", type = Executor.class, args = {MappedStatement.class, Object.class}),
    @Signature(method = "query", type = Executor.class,
        args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,
            BoundSql.class}), @Signature(method = "query", type = Executor.class,
    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class MultipleDataSourceInterceptor implements Interceptor {
    private static String dataSourceKey = null;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 每次请求，都去设置最新的数据源（至于数据源如何更换，则就是另一个话题了）
        if (dataSourceKey != null) {
            DataSourceHolder.setDataSource(dataSourceKey);
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
        //do nothing
    }

    public void registerCallback(String data) {
        dataSourceKey = data;
    }
}
</code></pre>
  <p data-lake-id="u94c55543" id="u94c55543"><span data-lake-id="u449e28e3" id="u449e28e3" class="lake-fontsize-11">​</span><br></p>
 </body>
</html>